#pragma once
#include <memory>

template<typename T>
class SharePtr
{
public:
	typedef T ElementType;
public:
	SharePtr()
		:m_refCount(nullptr)
		,m_objPtr(nullptr) {}

	explicit SharePtr(T * ptr) 
		:m_objPtr(nullptr)
		,m_refCount(nullptr)
	{
		this->_init(ptr);
	}

	SharePtr(const SharePtr<typename T> & sp)
		:m_objPtr(sp.m_objPtr)
		, m_refCount(sp.m_refCount)
	{
		if (m_objPtr==nullptr)
		{
			m_refCount = nullptr;
		}
		else
		{
			++(*m_refCount);
		}
	}

	template<typename S>
	SharePtr(const SharePtr<S> & ptr, T * p)
	{
		if (ptr.m_refCount)
		{
			++(*(ptr.m_refCount));
		}
		m_objPtr = p;
		m_refCount = ptr.m_refCount;
	}

	~SharePtr() 
	{
		this->_detach();
	}

	SharePtr<T>& operator=(const SharePtr<T> & sp)
	{
		if (sp.m_objPtr==m_objPtr)
		{
			return *this;
		}

		this->_detach();
		
		m_objPtr = sp.m_objPtr;
		m_refCount = sp.m_refCount;
		++(*m_refCount);

		return *sp;
	}

	T* operator->()const
	{
		return m_objPtr;
	}

	T& operator* ()const
	{
		return *m_objPtr;
	}

	void reset(T * ptr)
	{
		this->_detach();
		this->_init(ptr);
	}

	void reset()
	{
		this->_detach();
		m_objPtr = nullptr;
		m_refCount = nullptr;
	}

	T * get() const
	{
		return (m_objPtr);
	}

	explicit operator bool()const
	{
		return m_objPtr != nullptr;
	}

	bool operator==(const SharePtr<T> & rsp)
	{
		return this->m_objPtr == rsp.m_objPtr;
	}

	bool operator!=(const SharePtr<T> & rsp)
	{
		return this->m_objPtr != rsp.m_objPtr;
	}

	bool operator==(std::nullptr_t rp)
	{
		return this->m_objPtr == rp;
	}

	bool operator!=(std::nullptr_t rp)
	{
		return this->m_objPtr != rp;
	}

	void swap(SharePtr & r)
	{
		auto p = m_objPtr;
		auto c = m_refCount;
		m_objPtr = r.m_objPtr;
		m_refCount = r.m_refCount;
		r.m_objPtr = m_objPtr;
		r.m_refCount = m_refCount;
	}

private:

	void _init(T * p)
	{
		if (p!=nullptr)
		{
			m_refCount = new int;
			*m_refCount = 1;
		}
		m_objPtr = p;
	}

	void _detach() 
	{
		if (m_objPtr != nullptr)
		{
			if (m_refCount != nullptr)
			{
				--(*m_refCount);
				if (*m_refCount == 0)
				{
					delete m_refCount;
					delete m_objPtr;
					m_objPtr = nullptr;
					m_refCount = nullptr;
				}
			}
		}
	}

private:
	T *	 m_objPtr;
	int *m_refCount;
	template<class _T> friend class SharePtr;
};


template<typename T,typename ... Args>
SharePtr<T> makeSharePtr(Args&& ... args)
{
	return SharePtr<T>(new T(std::forward<Args>(args)...));
}

template<typename T,typename U>
SharePtr<T> staticPointerCast(const SharePtr<U> & r)
{
	typedef typename SharePtr<T>::ElementType Type;
	Type * p = static_cast<Type *>(r.get());
	return SharePtr<T>(r,p);
}

template<typename T,typename U>
SharePtr<T> dynamicPointerCast(const SharePtr<U> & r)
{
	typedef typename SharePtr<T>::ElementType Type;
	Type * p = dynamic_cast<Type *>(r.get());
	if (p!=nullptr)
	{
		return SharePtr<T>(r, p);
	}
	return SharePtr<T>();
}

template<typename T,typename U>
SharePtr<T> constPointerCast(const SharePtr<U> & r)
{
	typedef typename SharePtr<T>::ElementType Type;
	Type * p = const_cast<Type *>(r.get());
	return SharePtr<T>(r, p);
}

